package hku.cs.eclunuo.builders;

import hku.cs.eclunuo.LunuoConstant;
import hku.cs.eclunuo.LunuoLog;
import hku.cs.eclunuo.LunuoPlugin;
import hku.cs.eclunuo.run.LightRunner;
import hku.cs.eclunuo.run.MainRunner;
import hku.cs.eclunuo.util.LunuoLibHelper;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;

public class SootBuilder extends IncrementalProjectBuilder {

	public static final String BUILDER_ID = LunuoPlugin.PLUGIN_ID
			+ ".sootBuilder";

	private List<String> targetLst = new ArrayList<String>();
	private List<String> testLst = new ArrayList<String>();
	private List<String> copyLst = new ArrayList<String>();
	private List<String> removeLst = new ArrayList<String>();
	private List<IPath> srcPaths;

	private List<String> noInstruTargetPack = new ArrayList<String>();

	public SootBuilder() {
		// System.out.println(new Random().nextInt());
	}

	// private class ThreadA extends Thread
	// {
	// List<IJavaElement> shouldBuildClasses;
	// IProgressMonitor monitor;
	// public ThreadA(List<IJavaElement> klasses, IProgressMonitor m){
	// shouldBuildClasses = klasses;
	// monitor = m;
	// }
	//		
	// public void run(){
	// try {
	// Thread.sleep(100);
	// } catch (InterruptedException e1) {
	// // TODO Auto-generated catch block
	// e1.printStackTrace();
	// }
	// Runner runner = new Runner();
	// try {
	// runner.run(shouldBuildClasses, monitor);
	// } catch (CoreException e) {
	// // TODO Auto-generated catch block
	// e.printStackTrace();
	// }
	// }
	// }

	private void sootBuild(IProgressMonitor monitor) {
		// new ThreadA(shouldBuildClasses, monitor).start();
		// Display d= Display.getCurrent();

		// Runner runner = new Runner();
		// try {
		// runner.run(shouldBuildClasses, monitor);
		// } catch (CoreException e) {
		// // TODO Auto-generated catch block
		// e.printStackTrace();
		// }

		IResource resource = (IResource) getProject().getAdapter(
				IResource.class);
		String value = null;
		try {
			value = resource
					.getPersistentProperty(LunuoPlugin.LIGHT_BUILDER);
		} catch (CoreException e) {
			e.printStackTrace();
		}
		if (value == null || value.length() == 0)
			new LightRunner(getProject()).run(targetLst, testLst, copyLst,
					removeLst);
		else
			new MainRunner(getProject()).run(targetLst, testLst, copyLst,
				removeLst);

		// new ThreadA(shouldBuildClasses).run();

		// try {
		// ModalContext.run(new IRunnableWithProgress() {
		// public void run(IProgressMonitor monitor)
		// throws InvocationTargetException, InterruptedException {
		//					
		// Runner runner = new Runner();
		// try {
		// runner.run(shouldBuildClasses, monitor);
		// } catch (CoreException e) {
		// // TODO Auto-generated catch block
		// e.printStackTrace();
		// }
		// ModalContext.checkCanceled(monitor);
		// }
		// }, true, new NullProgressMonitor(), Display.getDefault());
		// } catch (InvocationTargetException e) {
		//
		// } catch (InterruptedException e) {
		// }

		// Job job = new Job("heloo"){
		// protected IStatus run(IProgressMonitor monitor){
		// Display.getDefault().asyncExec(
		// new Runnable(){
		// public void run(){
		// Runner runner = new Runner();
		// try {
		// runner.run(shouldBuildClasses);
		// } catch (CoreException e) {
		// // TODO Auto-generated catch block
		// e.printStackTrace();
		// }
		// }
		// }
		// );
		// return Status.OK_STATUS;
		// }
		// };
		// job.schedule();

		// IJavaProject ijp = JavaCore.create(file.getProject());
		// try {
		// if(ijp.getOutputLocation().isPrefixOf(JavaCore.create(file).getPath())){
		// Runner runner = new Runner();
		// try {
		// runner.run(JavaCore.create(file));
		// } catch (CoreException e) {
		// // TODO Auto-generated catch block
		// e.printStackTrace();
		// }
		// }
		// } catch (JavaModelException e1) {
		// // TODO Auto-generated catch block
		// e1.printStackTrace();
		// }
		//		
		// List<IJavaElement> changedRes = new ArrayList<IJavaElement>();
		// changedRes.add(JavaCore.create(file));
		//			
		// LunuoLaunchManager llm = new
		// LunuoLaunchManager(LunuoLaunchManager.INCBUILD, changedRes);
		// llm.run();

	}

	class SampleDeltaVisitor implements IResourceDeltaVisitor {
		// List<IPath> srcLst = LunuoLibHelper.getSourcPath(JavaCore
		// .create(getProject()));

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse
		 * .core.resources.IResourceDelta)
		 */
		public boolean visit(IResourceDelta delta) throws CoreException {
			IResource resource = delta.getResource();
			// Only dealt files under bin directory
			if (JavaCore.create(getProject()).getOutputLocation().isPrefixOf(
					resource.getFullPath()))

				switch (delta.getKind()) {
				case IResourceDelta.ADDED:
					// handle added resource
					classify(resource);
					break;
				case IResourceDelta.REMOVED:
					removeLst.add(resource.getFullPath().toString());
					break;
				case IResourceDelta.CHANGED:
					// handle changed resource
					classify(resource);
					break;

				}
			// return true to continue visiting children.
			return true;
		}
	}

	class SampleResourceVisitor implements IResourceVisitor {
		// List<IPath> srcLst = LunuoLibHelper.getSourcPath(JavaCore
		// .create(getProject()));

		public boolean visit(IResource resource) throws CoreException {

			// if (resource instanceof IFile) {
			// for (IPath s : srcLst)
			// if (s.isPrefixOf(resource.getFullPath()))
			// classify(resource);
			// }

			if (JavaCore.create(getProject()).getOutputLocation().isPrefixOf(
					resource.getFullPath())) {
				classify(resource);

				// return true to continue visiting children.

			}

			return true;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.core.internal.events.InternalBuilder#build(int,
	 * java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
	 */
	protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
			throws CoreException {

		targetLst.clear();
		testLst.clear();
		copyLst.clear();
		removeLst.clear();
		srcPaths = LunuoLibHelper.getSourcPathRaletiveProject(JavaCore
				.create(getProject()));

		IProject project = getProject();
		IResource resource = (IResource) project.getAdapter(IResource.class);
		try {
			String value = resource
					.getPersistentProperty(LunuoPlugin.FILTER_NOINSTRU_TARGET_PROPERTY);
			if (value == null) {
				noInstruTargetPack = Arrays.asList(new String[0]);
			} else
				noInstruTargetPack = Arrays.asList(value.split(";"));
		} catch (CoreException e) {
			LunuoLog.logError(e);
			noInstruTargetPack = Arrays.asList(new String[0]);
		}

		if (kind == FULL_BUILD) {
			fullBuild(monitor);
		} else {
			IResourceDelta delta = getDelta(getProject());
			if (delta == null) {
				fullBuild(monitor);
			} else {
				incrementalBuild(delta, monitor);
			}
		}
		sootBuild(monitor);
		return null;
	}

	protected void fullBuild(final IProgressMonitor monitor)
			throws CoreException {
		try {
			getProject().accept(new SampleResourceVisitor());
		} catch (CoreException e) {
		}
	}

	protected void incrementalBuild(IResourceDelta delta,
			IProgressMonitor monitor) throws CoreException {
		delta.accept(new SampleDeltaVisitor());
	}

	/*
	 * This selector classify the resource according to user setting, then do
	 * other works
	 */

	// public void classify(IResource resource) {
	//
	//
	// if (resource.getName().endsWith(".class")) {
	// IFile file = (IFile) resource;
	// ICompilationUnit javaFile = (ICompilationUnit) JavaCore
	// .create(file);
	// // System.out.println(javaFile.getHandleIdentifier());
	// IType javaType = javaFile.findPrimaryType();
	// String packName = javaType.getPackageFragment().getElementName();
	// if (packName.equals(""))
	// packName = LunuoConstant.DEFAULTPACKNAME;
	//
	// if (isTestSuite(javaType))
	// copyLst.add(resource.getFullPath().toString());
	// else if (isTestCase(javaType))
	// testLst.add(packName + "." + javaType.getElementName());
	// else if (noInstruTargetPack.contains(packName)) {
	// copyLst.add(resource.getFullPath().toString());
	// } else {
	// targetLst.add(packName + "." + javaType.getElementName());
	// }
	// } else
	// copyLst.add(resource.getFullPath().toString());
	// }
	public void classify(IResource resource) {

		// JavaCore.createCompilationUnitFrom

		if (resource instanceof IFile && resource.getName().endsWith(".class")) {
			IJavaProject javaProject = JavaCore.create(getProject());

			IPath filepath = resource.getFullPath();

			try {
				filepath = filepath.removeFirstSegments(javaProject
						.getOutputLocation().segmentCount());
			} catch (JavaModelException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			String yy = filepath.toOSString();
			yy = yy.substring(0, yy.lastIndexOf("."));
			yy = yy.replace(File.separatorChar, '.');

			IType javaType = null;
			try {
				String xx[] = yy.split("\\$");
				if (xx.length == 1)
					javaType = javaProject.findType(yy);
				else {
					// a patch
					javaType = javaProject.findType(xx[0]);

					// IType jt = javaProject.findType(xx[0]);
					// if(xx[1].matches("[1-9]"))
					// javaType = jt.getType("", Integer.parseInt(xx[1]));
					// else
					// javaType = jt.getType(xx[1]);

				}
			} catch (JavaModelException e) {
				e.printStackTrace();
			}

			// yy+=".java";
			//			
			// IType javaType = null;
			// for (IPath p : srcPaths) {
			// p = p.removeFirstSegments(getProject().getFullPath()
			// .segmentCount());
			// IPath xxx = p.append(yy);
			// IFile file = getProject().getFile(xxx);
			// ICompilationUnit icu = JavaCore.createCompilationUnitFrom(file);
			// if(icu==null)
			// continue;
			// javaType = icu.findPrimaryType();
			// }

			if(javaType==null)
				return;
			String packName = javaType.getPackageFragment().getElementName();
			if (packName.equals(""))
				packName = LunuoConstant.DEFAULTPACKNAME;

			if (isTestSuite(javaType))
				copyLst.add(resource.getFullPath().toString());
			else if (isTestCase(javaType))
				testLst.add(yy);
			else if (noInstruTargetPack.contains(packName)) {
				copyLst.add(resource.getFullPath().toString());
			} else {
				targetLst.add(yy);
			}
		} else
			copyLst.add(resource.getFullPath().toString());
	}

	private boolean isTestSuite(IType klass) {
		try {
			ITypeHierarchy hierarchy = klass
					.newSupertypeHierarchy(new NullProgressMonitor());

			for (IType type : hierarchy.getAllTypes()) {
				if ("junit.framework.TestSuite".equals(type
						.getFullyQualifiedName())) {
					return true;
				}
			}

		} catch (JavaModelException e) {
			e.printStackTrace();
		}

		return false;
	}

	private boolean isTestCase(IType klass) {
		try {
			ITypeHierarchy hierarchy = klass
					.newSupertypeHierarchy(new NullProgressMonitor());

			for (IType type : hierarchy.getAllTypes()) {
				if ("junit.framework.TestCase".equals(type
						.getFullyQualifiedName())) {
					return true;
				}
			}

		} catch (JavaModelException e) {
			e.printStackTrace();
		}

		return false;
	}

}